package com.rlyy.basic.mapping;


import lombok.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * url和mapping映射关系
 *
 * @author yuanqinglong
 * @since 2020/11/26 11:32
 */
@Component
public class RequestHandlerMapping implements ApplicationContextAware {

    static final Logger logger = LoggerFactory.getLogger(RequestHandlerMapping.class);

    /**
     * 请求路径和执行方法映射关系
     */
    private final Map<String, HandlerMethod> handlerMethods = new ConcurrentHashMap<>(128);


    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        logger.info("Start set mapping relation");
        Collection<Object> controllers = applicationContext.getBeansWithAnnotation(Controller.class).values();
        if (!CollectionUtils.isEmpty(controllers)) {
            for (Object controller : controllers) {
                for (Method declaredMethod : controller.getClass().getDeclaredMethods()) {
                    if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping requestMapping = declaredMethod.getAnnotation(RequestMapping.class);
                        if (requestMapping.value().length > 0) {
                            for (String url : requestMapping.value()) {
                                this.putMapping(url, new HandlerMethod(controller, declaredMethod));
                            }
                        } else {
                            this.putMapping(requestMapping.name(), new HandlerMethod(controller, declaredMethod));
                        }
                    }
                }
            }
        }
        logger.info("Set mapping relation completed");
    }

    /**
     * 通过url获取处理请求方法和实例
     *
     * @param url url
     * @return ReflexEntity
     */
    public HandlerMethod getHandlerMapping(final String url) {
        return handlerMethods.get(url);
    }

    /**
     * 判断url是否有映射关系
     *
     * @param url url
     * @return boolean or false
     */
    public boolean containsUrlMapping(final String url) {
        return handlerMethods.containsKey(url);
    }


    /**
     * 保存映射
     *
     * @param url           url
     * @param handlerMethod 处理程序方法
     */
    private void putMapping(String url, HandlerMethod handlerMethod) {
        if (StringUtils.isEmpty(url)){
            return;
        }
        if (handlerMethods.containsKey(url)) {
            throw new IllegalArgumentException("api名称{" + url + "}重复");
        }
        handlerMethods.put(url, handlerMethod);
    }
}

